引用是C++的概念,在C中没有此定义。
复制庞大而复杂的值有昂贵的开销。为了避免传递副本的开销,可将形参指定为引用类型。对引用形参的任何修改会直接影响实参本身。应将不需要修改相应实参的引用形参定义为 const 引用。
引用是变量的别名,变量直接使用变量对应的内存单元中的值,而引用是指向变量对应的内存单元;与指针不同,指针需要创建一个指针变量(局部变量),用于保存指向的变量的内存单元地址。而引用与引用指向的变量的内存地址是同一的。
引用本质上应该是一个常量指针,它的值初始化为其指向的变量,此后使用时无须解引用即直接表示其指向变量的值。它是一个常量,不能再指向其它变量,但指向的变量当然是一个可以更新的变量。
使用引用指向的变量的值时也与使用指针时不同,指针需解引用,引用不需要,可以如同变量一样使用。
引用有指针一样的威力,但语法要简单一些。
如有一个int的someInt变量,就可以定义一个引用:
int &rSomeRef = someInt;
rSomeRef与someInt的地址是同一的。
在C++中&现出在声明或等式的左边时,是引用,出现在等式的右边是,是取址;
引用比指针更容易使用和理解,引用的间接关系被隐藏,无须不断解除引用。
但引用不能为空,也不能重新赋值。指针提供了更大的灵活性,但使用起来更难。
按值传递时,将变量的备份而不是变量本身传递给函数,这让函数无法修改原始值。为了避免按值传递,一种方式是使用指针,这将传递原始变量的地址;另一种方式是使用引用,这将传递原始变量的别名。
按引用传递可以避免创建备份;
使用引用清晰、容易,然而,引用不能重新赋值,如果需要依次指向不同的对象,就必须使用指针。引用不能为NULL,因此如果要指向的对象可能为NULL,就必须使用指针,而不能使用引用。如果需要从堆中分配动态内存,也必须使用指针。
处理大量数据时,按引用传递比按值传递要好,可以更节省内存,因为不需要复制参数复本。
动态内存及动态数组,函数参数传址可以更新函数外部的值,同时用指针传址不需要再为参数开辟内存空间,当涉及到大指数据时,这样的设计更有效率。
在C、C++中,就引入了指针的概念来映射内存中的地址并表示这个地址的线性关系。
其它语言也通过其它的方式(如引用)来达成类似的操作。
引用如果是参数传递,则引用传递的是一个变量的内存地址,而不是值。
1 引用必须指向一个对象,C++要求引用必须初始化,并且没有NULL引用这种概念;
2 指针可以被重定向指向不同的对象,引用记录指向它初始化指向的对象;
3 基本上,当你可能会指向空(或什么都不指向)或可能会在不同时间指向不同对象的时候,使用指针!反之,则使用引用。
引用是对象的另一个名字,在实际程序中,引用主要用作函数的形式参数来使用。
在C++中,值传递是指将要传递的值作为一个副本传递。值传递过程中,被调函数的形参作为被调函数的局部变量处理,在内存的堆栈中开辟空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形参的任何操作都是作为局部变量进行,不会更改主调函数的实参变量的值。
引用传递传递的是引用对象的内存地址。在地址传递过程中,被调函数的形参也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。所以,被调函数对形参做的任何操作都会影响主调函数中的实参变量。
指针是一个实体,而引用仅是个别名;
引用使用时无需解引用(*),指针需要解引用;
引用只能在定义时被初始化一次,之后不可变;指针可变;
引用没有 const,指针有const;
引用不能为空,指针可以为空;
“sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
和引用的自增(++)运算意义不一样;
在内存分配上,程序为指针变量分配内存区域,而引用不需要分配内存区域。(应该也是分配4个字节的内存空间,引用的实质是一个常量指针。)
(1)引用在创建的同时必须初始化,即引用到一个有效的对象,而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
(2)不存在NULL引用,引用必须与合法的存储单元关联;而指针则可以是NULL。
(3)引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它和原始对象的绑定关系。
(4)引用的创建和销毁并不会调用类的拷贝构造函数。
(5)语言层面,引用的用法和对象一样。在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换。
不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象引用,显得很安全。const 指针仍然存在空指针,并且有可能产生野指针。总的来说,引用既具有指针的效率,又具有变量使用的方便性和直观性。
传值方式适合一般数值传送,并且不改变原数据,但要消耗内存空间;
传指针方式适合传递数组、指针,由于传递的是地址,所以直接操作会改变原数据;
引用方式和指针比较类似,是相对比较新的一种方式。一般情况下能用传址的就能用引用,而且使用引用更方便一点。
引用传递是地址传递的另一种更简单明了的实现方法
引用的定义:给一个变量取一个别名。
例:int i;
int &j=i;
j是i的别名,i与j是同一个内存单元。
引用实际上是一种隐式指针。每次使用引用变量时,可以不用书写间接引用运算符“*”,因而引用简化了程序。
C++引入引用的主要目的是将引用作为函数的参数。
定义引用时必须立即对它初始化,不能定义完成后再赋值。如:
int i; int &j; //错误 j=i;
为引用提供的初始值可以是一个变量或另一个引用。如:
int i=5; int &j1=i; int &j2=j1;
引用不可重新赋值,不可使其作为另一变量的别名,而指针可以指向另一个变量。
int i, k; int &j=i; j=&k;//错误
C++引入引用的主要目的是将引用作为函数的参数。
指针参数
void swap(int *m, int *n) { int temp; temp=*m; *m=*n; *n=temp; }
调用:swap(&x, &y)
引用参数
void swap(int &m, int &n) { int temp; temp=m; m=n; n=temp; }
调用:swap( x, y)
注意:实参必须是变量,而不能是一个表达式
在C++中,函数参数一般都采用引用传递。
利用引用传递的好处是减少函数调用时的开销。
如果在函数内不许改变参数的值,则参数用const限定。
对非const的参数,实际参数不能是常量或临时量。
返回引用的函数的主要用途
将函数用于赋值运算符的左边,即作为左值。
int a[] = {1, 3, 5, 7, 9}; int &index(int); //声明返回引用的函数 void main() { index(2) = 25; //将a[2]重新赋值为25 cout << index(2); } int &index(int j) {return a[j];} //函数是a[j]的一个引用
引用&是一个变量的别名;例如: int& b=a;//b声明为a的引用,b和a拥有相同的内存地址,可以通过改变b的值来改变a包含的值。
常量指针表示存储在指针中的地址不能修改;像这样的指针只能指向初始化时指定的地址,但是,地址中的内容不是常量,可以修改;(和指向常量的指针不同,指向常量的指针表示指向的内容是常量,不能够修改)。
至于引用和常量指针的区别:int& b=a; int* const p=&a; b是a的引用,他们拥有相同的内存地址,指针p存储的是变量a的地址,指针p自己拥有另一个内存地址,可以通过间接运算符来解除指针的引用:int c=*p;
另外一个区别,引用不占用额外的内存,而指针是占内存的(存疑,https://blog.csdn.net/dead_of_winter/article/details/1641373)。
引用比较安全,指针用好了很好,用不好就非常不好。
引用:引用主要用于函数参数,相对于指针和传值,引用变量不需要占用内存空间;相对于传值,不需要因为赋值而浪费时间(指针参数也有此优势);相对于指针,函数体内不需要使用解引用*符号,显得更简洁。
引用是与变量的固定关联,而指针却可以修改为指向其它空间。引用这种语法机制的定义主要是为了突破函数参数对数据的保护,我们知道,函数参数传递时是值传递,让引用做参数时传递的是地址值。
引用对象只能在初始化时设置目标对象,其后对引用对象的操作实际上都是对目标对象的操作。
如果这个指针变量需要定义为只读,即不能再另有指向,同时由编译器实现自动解引用,也就像使用一个变量一样来引用地址指向的值,则这样的指针变量在C++里称为引用,引用可以理解为一个变量的别名。
References are safer, more convenient versions of pointers. You declare references with the & declarator appended to the type name. References cannot be assigned to null (easily), and they cannot be reseated (or reassigned). These characteristics eliminate some bugs endemic to pointers.
为什么有的函数返回引用
如果一个函数的返回值是一个对象的值,它就被认为是一个常量,不能成为左值。
如果返回值为引用。由于引用是对象的别名,所以通过引用当然可以改变对象的值。
void setToNull(int *&tempPtr)
{
tempPtr = nullptr; // use 0 instead if not C++11
}
int* p;
&tempPtr = p; //tempPtr is a int*
pass by address is actually just passing an address by value!
Therefore, we can conclude that C++ really passes everything by value! The properties of pass by address (and reference) comes solely from the fact that we can dereference the passed address to change the argument, which we can not do with a normal value parameter!
Advantages of passing by address:
Pass by address allows a function to change the value of the argument, which is sometimes useful. Otherwise, const can be used to guarantee the function won’t change the argument. (However, if you want to do this with a non-pointer, you should use pass by reference instead).
Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
We can return multiple values from a function via out parameters.
Disadvantages of passing by address:
Because literals and expressions do not have addresses, pointer arguments must be normal variables.
All values must be checked to see whether they are null. Trying to dereference a null value will result in a crash. It is easy to forget to do this.
Because dereferencing a pointer is slower than accessing a value directly, accessing arguments passed by address is slower than accessing arguments passed by value.
When to use pass by address:
When passing built-in arrays (if you’re okay with the fact that they’ll decay into a pointer).
When passing a pointer and nullptr is a valid argument logically.
When not to use pass by address:
When passing a pointer and nullptr is not a valid argument logically (use pass by reference and dereference the pointer argument).
When passing structs or classes (use pass by reference).
When passing fundamental types (use pass by value).
As you can see, pass by address and pass by reference have almost identical advantages and disadvantages. Because pass by reference is generally safer than pass by address, pass by reference should be preferred in most cases.
When to use return by address:
When returning dynamically allocated memory
When returning function arguments that were passed by address
When not to use return by address:
When returning variables that were declared inside the function or parameters that were passed by value (use return by value)
When returning a large struct or class that was passed by reference (use return by reference)
When to use return by reference:
When returning a reference parameter
When returning an element from an array that was passed into the function
When returning a large struct or class that will not be destroyed at the end of the function (e.g. one that was passed in)
When not to use return by reference:
When returning variables that were declared inside the function or parameters that were passed by value (use return by value)
When returning a built-in array or pointer value (use return by address)
大多数情况下,应该使用引用而不是指针。对象的引用甚至可像指向对象的指针那样支持多态性。但也有一些情况下要求使用指针,一个例子是更改指向的位置,因为无法改变引用所指的变量。例如,动态分配内存时,应该将结果存储在指针而不是引用中。需要使用指针的另一种情况是可选参数,即指针参数可以定义为带默认值nullptr的可选函数,而引用参数不能这样定义。还有一种情况是要在容器中存储多态类型。
有一种方法可以判断使用指针还是引用作为参数和返回值:考虑谁拥有内存。如果接收变量的代码负责释放相关对象的内存,那么必须使用指向对象的指针,最好是智能指针,这是传递拥有权的推荐方式。如果接收变量的代码不需要释放内存,那么应该使用引用。
dereference ptr (get the value that ptr is pointing to)
One of the most annoying issues with C-style arrays is that in most cases they decay to pointers when evaluated. However, if a C-style array is passed by reference, this decaying does not happen.
#include <iostream> // Note: You need to specify the array size in the function declaration void printElements(int (&arr)[4]) { int length = sizeof(arr)/sizeof(arr[0]); // we can now do this since the array won't decay for (int i=0; i < length; ++i) { std::cout << arr[i] << " "; } }
引用从实现的角度来看,引用的底层都是通过指针来实现的。但是从逻辑的角度来看,引用又不是指针,我们看到引用,就要像看到变量本身一样,例如一个int类型变量,其引用也应该看成整数。
引用就是一个变量的别名,引用就是那个变量。引用一旦创建,就不能再代表其他变量了。如果此时再将某个变量赋值给这个引用,相当于赋值给引用代表的变量。参照前面第4点。
所以,对引用最好的理解方式,就是认为引用就是变量,不用关心为什么,编译器给我们制造了一个假象,就像一个人有大名和小名一样。你对引用做的任何操作,就相当于对变量本身做操作。
采用引用传递方式来传递一个指针非常有用。尤其当函数需要改变指针所存储的内存地址时,换句话说,当需要被传递的指针变量进行重定向时可以采用引用方式来传递它。 下面来看一个例子。
void first_bigger(int*& p, int threshold) { while (*p <=* threshold) p++; }
使用引用时还需要注意的是,首先,不能建立引用的数组。因为数组是某个数 据类型元素的集合,数组名表示该元素集合空间的起始地址,它自己不是一个名副其实的数 据类型。其次,引用本身不是一种数据类型,定义引用在概念上不产生内存空间,因此没有 引用的引用,也没有引用的指针。